﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace AuroraLightsFX
{
public class USBDevice
{
    // Fields
    private const uint CREATE_ALWAYS = 2;
    private const uint CREATE_NEW = 1;
    private const int DIGCF_ALLCLASSES = 4;
    private const int DIGCF_DEFAULT = 1;
    private const int DIGCF_DEVICEINTERFACE = 0x10;
    private const int DIGCF_PRESENT = 2;
    private const int DIGCF_PROFILE = 8;
    private const short FILE_ATTRIBUTE_NORMAL = 0x80;
    private const int FILE_SHARE_DELETE = 4;
    private const int FILE_SHARE_READ = 1;
    private const int FILE_SHARE_WRITE = 2;
    private const uint GENERIC_READ = 0x80000000;
    private const uint GENERIC_WRITE = 0x40000000;
    public const short INVALID_HANDLE_VALUE = -1;
    private const uint OPEN_EXISTING = 3;

    // Methods
    public static bool CloseDevice(IntPtr hDevice)
    {
        return CloseHandle(hDevice);
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern bool CloseHandle(IntPtr hHandle);
    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    private static void Delay(int milliseconds)
    {
        AutoResetEvent event2 = new AutoResetEvent(false);
        event2.Reset();
        event2.WaitOne(milliseconds, false);
    }

    public static bool FindDevice(int nVendorId, int nProductId, out string sDevicePath)
    {
        Guid guid;
        bool flag = false;
        sDevicePath = "";
        HidD_GetHidGuid(out guid);
        IntPtr hDevInfo = SetupDiGetClassDevsA(ref guid, IntPtr.Zero, IntPtr.Zero, 0x12);
        if (hDevInfo.ToInt32() == -1)
        {
            return false;
        }
        uint dw = 0;
        SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();

        deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); ;
        
        SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();


        deviceInterfaceDetailData.cbSize = 6;

        uint lastError = 0;
        while (!flag)
        {
            deviceInterfaceData.cbSize = 0x1c;
            if (!SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, dw, ref deviceInterfaceData))
            {
                lastError = GetLastError();
                return false;
            }
            dw++;
            uint dwRequiredSize = 0;
            if (SetupDiGetDeviceInterfaceDetailW(hDevInfo, ref deviceInterfaceData, ref deviceInterfaceDetailData, 0x1004, ref dwRequiredSize, IntPtr.Zero))
            {
                string devicePath = deviceInterfaceDetailData.DevicePath;
                IntPtr hDevice = CreateFile(devicePath, 0xc0000000, 3, IntPtr.Zero, 3, 0, IntPtr.Zero);
                if (hDevice.ToInt32() != -1)
                {
                    HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES();
                    attributes.Size = 10;
                    if (HidD_GetAttributes(hDevice, ref attributes) && ((attributes.VendorID == nVendorId) && (attributes.ProductID == nProductId)))
                    {
                        flag = true;
                        sDevicePath = devicePath;
                    }
                    CloseHandle(hDevice);
                }
                else
                {
                    lastError = GetLastError();
                }
            }
        }
        return flag;
    }

    [DllImport("kernel32.dll")]
    private static extern uint GetLastError();
    [DllImport("hid.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern bool HidD_GetAttributes(IntPtr hDevice, ref HIDD_ATTRIBUTES Attributes);
    [DllImport("hid.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern uint HidD_GetHidGuid(out Guid HidGuid);
    [DllImport("hid.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern uint HidD_GetProductString(out StringBuilder str);
    public static IntPtr OpenDevice(string sDevicePath)
    {
        return CreateFile(sDevicePath, 0xc0000000, 3, IntPtr.Zero, 3, 0, IntPtr.Zero);
    }

    [DllImport("kernel32.dll")]
    private static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] IntPtr lpOverlapped);
    public static int ReceiveData(IntPtr hDevice, byte[] abData, uint nDataLen, int nReportId)
    {
        uint num;
        if (!ReadFile(hDevice, abData, nDataLen, out num, IntPtr.Zero))
        {
            uint lastError = GetLastError();
            num = 0;
        }
        return (int) num;
    }

    public static bool ReceiveReport(IntPtr hDevice, byte[] abData, uint nReportLength)
    {
        uint num;
        if (!ReadFile(hDevice, abData, nReportLength, out num, IntPtr.Zero))
        {
            uint lastError = GetLastError();
            num = 0;
        }
        Delay(15);
        return (num == nReportLength);
    }

    public static bool SendData(IntPtr hDevice, byte[] abData, uint nDataLen, int nReportId)
    {
        byte[] abReport = new byte[nDataLen + 1];
        abReport[0] = (byte) nReportId;
        for (int i = 0; i < nDataLen; i++)
        {
            abReport[i + 1] = abData[i];
        }
        return SendReport(hDevice, abReport, nDataLen + 1);
    }

    public static bool SendReport(IntPtr hDevice, byte[] abReport, uint nReportLength)
    {
        uint lpNumberOfBytesWritten = 0;
        WriteFile(hDevice, abReport, nReportLength, out lpNumberOfBytesWritten, IntPtr.Zero);
        Delay(15);
        return (lpNumberOfBytesWritten == nReportLength);
    }

    [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);
    [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr pDeviceInfoData, ref Guid guid, uint dw, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
    [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern IntPtr SetupDiGetClassDevsA(ref Guid ClassGuid, IntPtr sEnumerator, IntPtr hWndParent, uint dwFlags);
    [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern bool SetupDiGetDeviceInterfaceDetailW(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, uint dwDeviceInterfaceDetailDataSize, ref uint dwRequiredSize, IntPtr DeviceInfoData);
    [DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
    private static extern bool SetupDiGetDeviceRegistryPropertyA(IntPtr hDevInfo, ref SP_DEVINFO_DATA DevInfoData, uint dwProperty, ref uint dwPropertyRefDataType, ref byte[] bPropertyBuffer, uint dwPropertyBufferSize, ref uint dwRequiredSize);
    [DllImport("kernel32.dll")]
    private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] IntPtr lpOverlapped);

    // Nested Types
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
    private struct HIDD_ATTRIBUTES
    {
        public uint Size;
        public ushort VendorID;
        public ushort ProductID;
        public ushort VersionNumber;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct SP_DEVICE_INTERFACE_DATA
    {
        public int cbSize;
        public Guid InterfaceClassGuid;
        public IntPtr Flags;
        public IntPtr Reserved;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack=1)]
    private struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        public uint cbSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=0x1000)]
        public string DevicePath;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    private struct SP_DEVINFO_DATA
    {
        public uint cbSize;
        public Guid ClassGuid;
        public uint DevInst;
        public IntPtr Reserved;
    }
}

 
 

}
